package com.cq.hd.merchant.biz;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cq.hd.common.constant.Constant;
import com.cq.hd.common.constant.RedisKeyConstant;
import com.cq.hd.common.entity.BusinessInfo;
import com.cq.hd.common.entity.MerchantAppLoginUser;
import com.cq.hd.common.entity.MerchantUserInfo;
import com.cq.hd.common.entity.mq.MerchantLoginRecordMsg;
import com.cq.hd.common.entity.wx.dto.WxLoginMobileDto;
import com.cq.hd.common.entity.wx.vo.Code2SessionVo;
import com.cq.hd.common.enums.ResponseEnums;
import com.cq.hd.common.enums.biz.SmsTypeEnum;
import com.cq.hd.common.response.Throw;
import com.cq.hd.common.utils.*;
import com.cq.hd.member.api.SmsApi;
import com.cq.hd.member.api.TbBusinessApi;
import com.cq.hd.member.api.TbMerchantApi;
import com.cq.hd.member.api.dto.MerchantLoginDto;
import com.cq.hd.member.api.dto.MerchantSmsLoginDto;
import com.cq.hd.member.api.dto.MerchantUpdateDto;
import com.cq.hd.member.api.dto.SmsVerifyDto;
import com.cq.hd.member.api.vo.BusinessPageVo;
import com.cq.hd.member.api.vo.MerchantLoginVo;
import com.cq.hd.member.api.vo.MerchantVo;
import com.cq.hd.merchant.config.LockUtils;
import com.cq.hd.merchant.config.WxConfig;
import com.cq.hd.merchant.mq.MerchantLoginRecordMsgProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class AuthBiz {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private TbMerchantApi merchantApi;

    @Autowired
    private TbBusinessApi businessApi;

    @Autowired
    private SmsApi smsApi;

    @Autowired
    private MerchantLoginRecordMsgProvider merchantLoginRecordMsgProvider;

    @Autowired
    private WxConfig wxConfig;

    public Code2SessionVo code2session(String code, HttpServletRequest request) {
        String result = wxConfig.getOpenIdAndSessionKey(code);
        JSONObject openIdAndSessionKey = JSON.parseObject(result);
        String openid = openIdAndSessionKey.getString("openid");
        String sessionKey = openIdAndSessionKey.getString("session_key");
        if (StringUtils.isBlank(openid) || StringUtils.isBlank(sessionKey)) {
            log.error("code2session获取失败，result:{}", result);
            Throw.isBusinessException("登录失败");
        }

        // 根据openId查库，存在表示已经注册，返回token，否则返回openId
//        MerchantVo merchantVo = merchantApi.getByOpenId(openid).unpack();
//        if (merchantVo != null) {
//            if (merchantVo.getState() == 2) {
//                Throw.isBusinessException(ResponseEnums.USER_NOT_FOUND);
//            }
//
//            MerchantUserInfo merchantUserInfo = new MerchantUserInfo();
//            BeanUtils.copyProperties(merchantVo, merchantUserInfo);
//            String merchantIdRedis = String.valueOf(merchantVo.getId());
//
//            // 判断商户是否是商家
//            BusinessPageVo businessPageVo = businessApi.getByMobile(merchantVo.getMobile()).unpack();
//            if (businessPageVo != null) {
//                BusinessInfo businessInfo = new BusinessInfo();
//                BeanUtils.copyProperties(businessPageVo, businessInfo);
//                merchantUserInfo.setBusinessInfo(businessInfo);
//            }
//
//            // 更新商户最后登录时间和登录ip
//            MerchantUpdateDto merchantUpdateDto = new MerchantUpdateDto();
//            merchantUpdateDto.setId(Long.parseLong(merchantIdRedis));
//            merchantUpdateDto.setLastLoginIp(IpUtil.getIpAddr(request));
//            merchantUpdateDto.setLastLoginTime(LocalDateTime.now());
//            merchantUpdateDto.setUpdateTime(LocalDateTime.now());
//            merchantApi.updateMerchant(merchantUpdateDto).isSuccess();
//
//            // 将用户详细信息缓存到Redis
//            redisUtil.set(String.format(RedisKeyConstant.MERCHANT_INFO_KEY, merchantIdRedis), JSON.toJSONString(merchantUserInfo), Constant.USER_LOGIN_TIMEOUT, TimeUnit.SECONDS);
//            redisUtil.set(String.format(RedisKeyConstant.MERCHANT_MOBILE_KEY, merchantUserInfo.getMobile()), merchantIdRedis);
//
//            // 生成随机密钥
//            String logKey = StringUtils.getRandomNumString(8);
//            // 生成token
//            String token = JwtUtil.createToken(logKey, merchantIdRedis);
//            redisUtil.set(String.format(RedisKeyConstant.MERCHANT_LOGIN_KEY, merchantIdRedis), logKey, Constant.USER_LOGIN_TIMEOUT, TimeUnit.SECONDS);
//
//            // 发送MQ消息，异步添加商户登录记录
//            MerchantLoginRecordMsg merchantLoginRecordMsg = new MerchantLoginRecordMsg();
//            merchantLoginRecordMsg.setMerchantId(merchantVo.getId());
//            merchantLoginRecordMsg.setLoginDate(LocalDateTime.now());
//            merchantLoginRecordMsg.setLoginIp(IpUtil.getIpAddr(request));
//            // 登录方式，默认1:手机号登录 2:用户名密码登录
//            merchantLoginRecordMsg.setLoginMode(1);
//            merchantLoginRecordMsg.setCreateTime(LocalDateTime.now());
//            merchantLoginRecordProvider.sendMsg(merchantLoginRecordMsg);
//
//            Code2SessionVo code2SessionVo = new Code2SessionVo();
//            code2SessionVo.setToken(token);
//            return code2SessionVo;
//        }

        redisUtil.hmSet(String.format(RedisKeyConstant.MERCHANT_WX_OPEN_ID_KEY, openid), "sessionKey", sessionKey);
        redisUtil.expire(String.format(RedisKeyConstant.MERCHANT_WX_OPEN_ID_KEY, openid), 30, TimeUnit.MINUTES);

        Code2SessionVo code2SessionVo = new Code2SessionVo();
        code2SessionVo.setOpenId(openid);

        return code2SessionVo;
    }

    public String getWxMobile(WxLoginMobileDto wxLoginMobileDto) {
        String openId = wxLoginMobileDto.getOpenId();
        Object sessionKeyObj = redisUtil.hGet(String.format(RedisKeyConstant.MERCHANT_WX_OPEN_ID_KEY, openId), "sessionKey");
        if (sessionKeyObj == null) {
            log.error("getWxMobile获取手机号失败，sessionKey为空");
            Throw.isBusinessException("获取手机号失败");
        }

        JSONObject jsonObject = AES.wxDecrypt(wxLoginMobileDto.getEncryptedData(), (String) sessionKeyObj, wxLoginMobileDto.getIv());
        String phoneNumber = jsonObject.getString("phoneNumber");
        if (StringUtils.isBlank(phoneNumber)) {
            log.error("getWxMobile获取手机号失败，解析手机号为空");
            Throw.isBusinessException("获取手机号失败");
        }

        redisUtil.hmSet(String.format(RedisKeyConstant.MERCHANT_WX_OPEN_ID_KEY, openId), "mobile", phoneNumber);
        redisUtil.expire(String.format(RedisKeyConstant.MERCHANT_WX_OPEN_ID_KEY, openId), 30, TimeUnit.MINUTES);

        return phoneNumber;
    }


    public MerchantLoginVo login(MerchantLoginDto merchantLoginDto, HttpServletRequest request) {
        // 获取登录IP地址
        String mobile = merchantLoginDto.getMobile();
        String userPass = merchantLoginDto.getUserPass();

        // 对手机号加锁
        String lockKey = MessageFormat.format(Constant.LOCK_FORMAT, "merchantLoginByUserName:" + mobile);
        try {
            LockUtils.blockLock(lockKey, Constant.AUTO_UNLOCK_SECOND);

            // 通过手机号查询商户信息
            MerchantVo merchantVo = merchantApi.getByMobile(mobile).unpack();
            if (merchantVo == null) {
                Throw.isBusinessException(ResponseEnums.USER_NOT_FOUND);
            }

            // 校验是否被封禁
            if (merchantVo.getState() == 2) {
                Throw.isBusinessException(ResponseEnums.USER_NOT_FOUND);
            }

            // 校验密码是否正确
            BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
            if (!encoder.matches(userPass, merchantVo.getLoginPass())) {
                Throw.isBusinessException(ResponseEnums.USER_PWD_FAILED);
            }

            MerchantUserInfo merchantUserInfo = new MerchantUserInfo();
            BeanUtils.copyProperties(merchantVo, merchantUserInfo);
            String merchantIdRedis = String.valueOf(merchantVo.getId());

            // 判断商户是否是商家
            BusinessPageVo businessPageVo = businessApi.getByMobile(merchantVo.getMobile()).unpack();
            if (businessPageVo != null) {
                BusinessInfo businessInfo = new BusinessInfo();
                BeanUtils.copyProperties(businessPageVo, businessInfo);
                merchantUserInfo.setBusinessInfo(businessInfo);
            }

            // 更新商户最后登录时间和登录ip
            MerchantUpdateDto merchantUpdateDto = new MerchantUpdateDto();
            merchantUpdateDto.setId(Long.parseLong(merchantIdRedis));
            merchantUpdateDto.setLastLoginIp(IpUtil.getIpAddr(request));
            merchantUpdateDto.setLastLoginTime(LocalDateTime.now());
            merchantUpdateDto.setUpdateTime(LocalDateTime.now());
            merchantApi.updateMerchant(merchantUpdateDto).isSuccess();

            merchantUserInfo.setPwdSetState(!StringUtils.isBlank(merchantVo.getLoginPass()));

            // 将用户详细信息缓存到Redis
            redisUtil.set(String.format(RedisKeyConstant.MERCHANT_INFO_KEY, merchantIdRedis), JSON.toJSONString(merchantUserInfo), Constant.USER_LOGIN_TIMEOUT, TimeUnit.SECONDS);
            redisUtil.set(String.format(RedisKeyConstant.MERCHANT_MOBILE_KEY, merchantUserInfo.getMobile()), merchantIdRedis);

            // 生成随机密钥
            String logKey = StringUtils.getRandomNumString(8);
            // 生成token
            String lookupLogKey = UUID.randomUUID().toString().replaceAll("-", "");
            String token = JwtUtil.createToken(logKey, merchantIdRedis, lookupLogKey);
            redisUtil.set(String.format(RedisKeyConstant.MERCHANT_LOGIN_KEY, lookupLogKey), logKey, Constant.USER_LOGIN_TIMEOUT, TimeUnit.SECONDS);

            // 发送MQ消息，异步添加商户登录记录
            MerchantLoginRecordMsg merchantLoginRecordMsg = new MerchantLoginRecordMsg();
            merchantLoginRecordMsg.setMerchantId(merchantVo.getId());
            merchantLoginRecordMsg.setLoginDate(LocalDateTime.now());
            merchantLoginRecordMsg.setLoginIp(IpUtil.getIpAddr(request));
            // 登录方式，默认1:手机号登录 2:用户名密码登录
            merchantLoginRecordMsg.setLoginMode(2);
            merchantLoginRecordMsg.setCreateTime(LocalDateTime.now());
            merchantLoginRecordMsgProvider.sendMsg(merchantLoginRecordMsg);

            MerchantLoginVo merchantLoginVo = new MerchantLoginVo();
            merchantLoginVo.setToken(token);

            return merchantLoginVo;
        } finally {
            LockUtils.unlock(lockKey);
        }
    }

    public MerchantLoginVo loginBySmsCode(MerchantSmsLoginDto merchantSmsLoginDto, HttpServletRequest request) {
        String smsCode = merchantSmsLoginDto.getSmsCode();
        String mobile = merchantSmsLoginDto.getMobile();

        // 对手机号加锁
        String lockKey = MessageFormat.format(Constant.LOCK_FORMAT, "merchantLoginByMobile:" + mobile);
        try {
            LockUtils.blockLock(lockKey, Constant.AUTO_UNLOCK_SECOND);

            // 判断短信验证码是否正确
            SmsVerifyDto smsVerifyDto = new SmsVerifyDto();
            smsVerifyDto.setCode(smsCode);
            smsVerifyDto.setMobile(mobile);
            smsVerifyDto.setType(SmsTypeEnum.SMS_MERCHANT_LOGIN.getValue());
            smsApi.verify(smsVerifyDto).isSuccess();

            // 通过手机号查询商户信息
            MerchantVo merchantVo = merchantApi.getByMobile(mobile).unpack();
            // 判断用户是否是新用户
            if (merchantVo == null) {
                Throw.isBusinessException(ResponseEnums.USER_NOT_FOUND);
            }

            MerchantUserInfo merchantUserInfo = new MerchantUserInfo();
            BeanUtils.copyProperties(merchantVo, merchantUserInfo);
            String merchantIdRedis = String.valueOf(merchantVo.getId());

            // 判断商户是否是商家
            BusinessPageVo businessPageVo = businessApi.getByMobile(merchantVo.getMobile()).unpack();
            if (businessPageVo != null) {
                BusinessInfo businessInfo = new BusinessInfo();
                BeanUtils.copyProperties(businessPageVo, businessInfo);
                merchantUserInfo.setBusinessInfo(businessInfo);
            }

            // 更新商户最后登录时间和登录ip
            MerchantUpdateDto merchantUpdateDto = new MerchantUpdateDto();
            merchantUpdateDto.setId(Long.parseLong(merchantIdRedis));
            merchantUpdateDto.setLastLoginIp(IpUtil.getIpAddr(request));
            merchantUpdateDto.setLastLoginTime(LocalDateTime.now());
            merchantUpdateDto.setUpdateTime(LocalDateTime.now());
            merchantApi.updateMerchant(merchantUpdateDto).isSuccess();

            merchantUserInfo.setPwdSetState(!StringUtils.isBlank(merchantVo.getLoginPass()));

            // 将用户详细信息缓存到Redis
            redisUtil.set(String.format(RedisKeyConstant.MERCHANT_INFO_KEY, merchantIdRedis), JSON.toJSONString(merchantUserInfo), Constant.USER_LOGIN_TIMEOUT, TimeUnit.SECONDS);
            // 将userId缓存到Redis
            redisUtil.set(String.format(RedisKeyConstant.MERCHANT_MOBILE_KEY, mobile), merchantIdRedis);

            // 生成随机密钥
            String logKey = StringUtils.getRandomNumString(8);
            // 生成token
            String lookupLogKey = UUID.randomUUID().toString().replaceAll("-", "");
            String token = JwtUtil.createToken(logKey, merchantIdRedis, lookupLogKey);
            redisUtil.set(String.format(RedisKeyConstant.MERCHANT_LOGIN_KEY, lookupLogKey), logKey, Constant.USER_LOGIN_TIMEOUT, TimeUnit.SECONDS);

            // 发送MQ消息，异步添加商户登录记录
            MerchantLoginRecordMsg merchantLoginRecordMsg = new MerchantLoginRecordMsg();
            merchantLoginRecordMsg.setMerchantId(Long.valueOf(merchantIdRedis));
            merchantLoginRecordMsg.setLoginDate(LocalDateTime.now());
            merchantLoginRecordMsg.setLoginIp(IpUtil.getIpAddr(request));
            // 登录方式，默认1:手机号登录 2:微信登录
            merchantLoginRecordMsg.setLoginMode(1);
            merchantLoginRecordMsg.setCreateTime(LocalDateTime.now());
            merchantLoginRecordMsgProvider.sendMsg(merchantLoginRecordMsg);

            MerchantLoginVo merchantLoginVo = new MerchantLoginVo();
            merchantLoginVo.setToken(token);

            return merchantLoginVo;
        } finally {
            LockUtils.unlock(lockKey);
        }
    }

    public boolean logout(String token, MerchantAppLoginUser merchantAppLoginUser) {
        String lookupLogKey = String.valueOf(merchantAppLoginUser.getMerchantId());
        try {
            lookupLogKey = JwtUtil.getAudience(token, 1);
        } catch (Exception ignored) {
        }
        redisUtil.del(String.format(RedisKeyConstant.MERCHANT_LOGIN_KEY, lookupLogKey));
        return true;
    }
}
